package com.kelin.calendarlistview.library;

import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;

import ohos.utils.PlainIntArray;

/**
 * SectionedBaseAdapter
 */
public abstract class SectionedBaseAdapter extends BaseItemProvider implements PinnedSectionedHeaderAdapter {

    /**
     * Holds the calculated values of @{link getPositionInSectionForPosition}
     */
    private final PlainIntArray mSectionPositionCache;
    /**
     * Holds the calculated values of @{link getSectionForPosition}
     */
    private final PlainIntArray mSectionCache;
    /**
     * Holds the calculated values of @{link getCountForSection}
     */
    private final PlainIntArray mSectionCountCache;

    /**
     * Caches the item count
     */
    private int mCount;
    /**
     * Caches the section count
     */
    private int mSectionCount;

    /**
     * SectionedBaseAdapter instance
     *
     */
    public SectionedBaseAdapter() {
        super();
        mSectionCache = new PlainIntArray();
        mSectionPositionCache = new PlainIntArray();
        mSectionCountCache = new PlainIntArray();
        mCount = -1;
        mSectionCount = -1;
    }

    @Override
    public void notifyDataSetItemChanged(int position) {
        super.notifyDataSetItemChanged(position);
    }

    @Override
    public void notifyDataChanged() {
        mSectionCache.clear();
        mSectionPositionCache.clear();
        mSectionCountCache.clear();
        mCount = -1;
        mSectionCount = -1;
        super.notifyDataChanged();
    }

    @Override
    public void notifyDataInvalidated() {
        mSectionCache.clear();
        mSectionPositionCache.clear();
        mSectionCountCache.clear();
        mCount = -1;
        mSectionCount = -1;
        super.notifyDataInvalidated();
    }

    @Override
    public final int getCount() {
        if (mCount >= 0) {
            return mCount;
        }
        int count = 0;
        for (int i = 0; i < internalGetSectionCount(); i++) {
            count += internalGetCountForSection(i);
            count++; // for the header view
        }
        mCount = count;
        return count;
    }

    @Override
    public final Object getItem(int position) {
        return getItem(getSectionForPosition(position), getPositionInSectionForPosition(position));
    }

    @Override
    public final long getItemId(int position) {
        return getItemId(getSectionForPosition(position), getPositionInSectionForPosition(position));
    }

    @Override
    public final Component getComponent(int position, Component convertView, ComponentContainer parent) {
        if (isSectionHeader(position)) {
            return getSectionHeaderView(getSectionForPosition(position), convertView, parent);
        }
        return getItemView(getSectionForPosition(position), getPositionInSectionForPosition(position), convertView, parent);
    }

    @Override
    public int getItemComponentType(int position) {
        if (isSectionHeader(position)) {
            return getItemViewTypeCount() + getSectionHeaderViewType(getSectionForPosition(position));
        }
        return getItemViewType(getSectionForPosition(position), getPositionInSectionForPosition(position));
    }

    @Override
    public int getComponentTypeCount() {
        return getItemViewTypeCount() + getSectionHeaderViewTypeCount();
    }

    /**
     * getSectionForPosition
     * @param position position
     * @return int
     */
    public final int getSectionForPosition(int position) {
        // first try to retrieve values from cache
        int cachedSection = mSectionCache.get(position, -1);
        if (cachedSection != -1) {
            return cachedSection;
        }
        int sectionStart = 0;
        for (int i = 0; i < internalGetSectionCount(); i++) {
            int sectionCount = internalGetCountForSection(i);
            int sectionEnd = sectionStart + sectionCount + 1;
            if (position >= sectionStart && position < sectionEnd) {
                mSectionCache.put(position, i);
                return i;
            }
            sectionStart = sectionEnd;
        }
        return 0;
    }

    /**
     * getPositionInSectionForPosition
     * @param position position
     * @return int
     */
    public int getPositionInSectionForPosition(int position) {
        // first try to retrieve values from cache
        int cachedPosition = mSectionPositionCache.get(position, -1);
        if (cachedPosition != -1) {
            return cachedPosition;
        }
        int sectionStart = 0;
        for (int i = 0; i < internalGetSectionCount(); i++) {
            int sectionCount = internalGetCountForSection(i);
            int sectionEnd = sectionStart + sectionCount + 1;
            if (position >= sectionStart && position < sectionEnd) {
                int positionInSection = position - sectionStart - 1;
                mSectionPositionCache.put(position, positionInSection);
                return positionInSection;
            }
            sectionStart = sectionEnd;
        }
        return 0;
    }

    /**
     * isSectionHeader
     * @param position position
     * @return boolean
     */
    public final boolean isSectionHeader(int position) {
        int sectionStart = 0;
        for (int i = 0; i < internalGetSectionCount(); i++) {
            if (position == sectionStart) {
                return true;
            } else if (position < sectionStart) {
                return false;
            }
            sectionStart += internalGetCountForSection(i) + 1;
        }
        return false;
    }

    /**
     * getItemViewType
     * @param section section
     * @param position position
     * @return int
     */
    public int getItemViewType(int section, int position) {
        return 0;
    }

    /**
     * getItemViewTypeCount
     * @return int
     */
    public int getItemViewTypeCount() {
        return 1;
    }

    /**
     * getSectionHeaderViewType
     * @param section section
     * @return int
     */
    public int getSectionHeaderViewType(int section) {
        return 0;
    }

    /**
     * getSectionHeaderViewTypeCount
     * @return int
     */
    public int getSectionHeaderViewTypeCount() {
        return 1;
    }

    /**
     * getItem
     * @param section section
     * @param position position
     * @return Object
     */
    public abstract Object getItem(int section, int position);

    /**
     * getItemId
     * @param section section
     * @param position position
     * @return long
     */
    public abstract long getItemId(int section, int position);

    /**
     * getSectionCount
     * @return int
     */
    public abstract int getSectionCount();

    /**
     * getCountForSection
     * @param section section
     * @return int
     */
    public abstract int getCountForSection(int section);

    /**
     * getItemView
     * @param section section
     * @param position position
     * @param convertView Component
     * @param parent ComponentContainer
     * @return Component
     */
    public abstract Component getItemView(int section, int position, Component convertView, ComponentContainer parent);

    /**
     * getSectionHeaderView
     * @param section section
     * @param convertView Component
     * @param parent ComponentContainer
     * @return Component
     */
    public abstract Component getSectionHeaderView(int section, Component convertView, ComponentContainer parent);

    private int internalGetCountForSection(int section) {
        int cachedSectionCount = mSectionCountCache.get(section, -1);
        if (cachedSectionCount != -1) {
            return cachedSectionCount;
        }
        int sectionCount = getCountForSection(section);
        mSectionCountCache.put(section, sectionCount);
        return sectionCount;
    }

    private int internalGetSectionCount() {
        if (mSectionCount >= 0) {
            return mSectionCount;
        }
        mSectionCount = getSectionCount();
        return mSectionCount;
    }

}
